package abel.wlmr.model.type;

import abel.util.HexString;
import abel.util.StdBcdNumber;
import abel.wlmr.model.WlmrBaseUnit;
import abel.wlmr.model.WlmrException;
import io.netty.buffer.ByteBuf;

public class MeterConfigBody extends WlmrBaseUnit {

    public static final short FIXED_LENGTH = 2 + 2 + 1 + 1 + 7 + 6 + 1 + 1 + 6 + 1 + 1;

    /**
     * 远传表计装置序号：数值范围 1～2040。
     */
    private short meterNo;

    /**
     * 所属测量点号：数值范围 0～2040，如为“0”，表示本序号的远传表计装置未指向任何测量点，
     * 即被“删除”。
     */
    private short daNo;

    /**
     * 通信速率及通信端口号：
     * ● D7～D5 编码表示远传表计与终端的通信波特率， 1～7 依次表示 600、 1200、 2400、 4800、 7200、
     * 9600、 19200； 0：表示无需设置或使用默认值。
     * ● D4～D0 编码表示远传表计装置与终端连接所对应的终端通信端口号，数值范围 1～31，其他值无
     * 效。定义见附录 I。
     */
    private BaudPortField baudPort = new BaudPortField();

    /**
     * 通信协议类型：数值范围 0～255，其中 0：表示终端无需对本序号的远传表计装置进行抄表； 1：
     * DL/T 645—1997.； 2：交流采样装置通信协议； 30： DL/T 645—2007； 31：“串行接口连接窄带
     * 低压载波通信模块”接口协议； 32： CJ/T 188 户用计量仪表数据传输技术条件； 33： GB/T26831.2
     * 社区能源计量超收系统规范 第 2 部分和 GB/T26831.2 社区能源计量超收系统规范 第 3 部分；
     * 其他：备用。
     */
    private ProtocolType protocolType = ProtocolType.GBT26831_2;

    /**
     * 通信地址 见附录 C.33 7
     * 通信地址：数值范围 0～999999999999。
     */
    private StdBcdNumber meterAddr;

    /**
     * 通信密码 BIN 6
     * 通信密码：终端与远传表通信的密码。
     */
    private HexString meterPwd;

    /**
     * 费率个数
     * ● D7～D6：备用。
     * ● D5～D0：编码表示通信接入的测量点的费率个数，数值范围 1～48。
     */
    private byte priceCnt = 1;

    /**
     * 计量示值的整数位及小数位个数：
     * ● D7～D4：备用
     * ● D3～D2：编码表示通信接入的远传表计示值的整数位个数，数值范围 0～3 依次表示 5～8 位整数。
     * ● D1～D0：编码表示通信接入的远传表计示值的小数位个数，数值范围 0～3 依次表示 1～4 位小数。
     */
    private DecFormat decFormat = new DecFormat();

    /**
     * 所属采集器通信地址 见附录 C.12 6
     * 所属采集器通信地址：数值范围 0～999999999999，如为“0”，表示本远传表未通过采集器连
     * 接到终端（或称“台区集中器”），而是直接连接到了终端（或称“台区集中器”）。
     */
    private StdBcdNumber parentAddr;

    /**
     * 用户大类号：
     * 编码表示本远传表所属的用户大类号，数值范围 0～255。定义见附录 I。
     */
    private UserTopCategory topCat = UserTopCategory.WaterResident;

    /**
     * 用户小类号：
     * 编码表示本远传表所属的用户小类号，数值范围 0～255。定义见附录 I
     */
    private UserDetailCategory detailCat = UserDetailCategory.WaterWirelessCold;

    /** will be configured to station again if not set. Never persisted in config file */
    private volatile boolean configured;

    /**
     * @param thisLen
     */
    public MeterConfigBody() {
        super(FIXED_LENGTH);
    }

    @Override
    public ByteBuf appendBuf(ByteBuf buf) {
        if (meterNo < 1 || meterNo > 2040) {
            throw new IllegalArgumentException("meter no should be [1,2040] but: " + meterNo);
        }
        if (daNo < 0 || daNo > 2040) {
            throw new IllegalArgumentException("DA no should be [1,2040] but: " + daNo);
        }
        if (meterAddr == null || meterAddr.length() != 7) {
            throw new IllegalArgumentException("meter addr should be 7 long: " + meterAddr);
        }
        if (meterPwd == null || meterPwd.length() != 6) {
            throw new IllegalArgumentException("meter pwd should be 6 long: " + meterPwd);
        }
        if (parentAddr == null || parentAddr.length() != 6) {
            throw new IllegalArgumentException("parent addr should be 6 long: " + parentAddr);
        }

        buf.writeShortLE(meterNo);
        buf.writeShortLE(daNo);
        buf.writeByte(baudPort.getValue());
        buf.writeByte(protocolType.getValue());
        buf.writeBytes(meterAddr.toBytes());
        buf.writeBytes(meterPwd.getBs());
        buf.writeByte(priceCnt);
        buf.writeByte(decFormat.getValue());
        buf.writeBytes(parentAddr.toBytes());
        buf.writeByte(topCat.getValue());
        buf.writeByte(detailCat.getValue());

        return buf;
    }

    @Override
    public int parseBuf(ByteBuf buf) throws WlmrException {
        if (checkBuffer(buf) < 0) {
            return -1;
        }

        meterNo = buf.readShortLE();
        daNo = buf.readShortLE();
        baudPort.parseValue(buf.readByte());
        protocolType = ProtocolType.findEnumByValue(buf.readByte());
        meterAddr = new StdBcdNumber();
        meterAddr.populateBytes(buf, 7);
        meterPwd = new HexString(buf, 6);
        priceCnt = buf.readByte();
        decFormat.parseValue(buf.readByte());
        parentAddr = new StdBcdNumber();
        parentAddr.populateBytes(buf, 6);
        topCat = UserTopCategory.findCatByValue(buf.readByte());
        detailCat = UserDetailCategory.findCatByValue(buf.readByte());

        if (meterNo < 1 || meterNo > 2040) {
            throw new WlmrException("meter no should be [1,2040] but: " + meterNo);
        }
        if (daNo < 0 || daNo > 2040) {
            throw new WlmrException("DA no should be [1,2040] but: " + daNo);
        }
        if (priceCnt < 1 || priceCnt > 48) {
            throw new WlmrException("price count should be [1, 48] but: " + priceCnt);
        }

        return getGuessedLength();
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.WlmrDataPart#getFinalLen()
     */
    @Override
    public short getFinalLen() {
        return FIXED_LENGTH;
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.WlmrDataPart#getMinLength()
     */
    @Override
    public int getMinLength() {
        return FIXED_LENGTH;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return String.format(
            "{meterNo:%s, daNo:%s, baudPort:%s, protocolType:%s, meterAddr:%s, meterPwd:%s, priceCnt:%s, decFormat:%s, parentAddr:%s, topCat:%s, detailCat:%s, configured:%s}",
            meterNo, daNo, baudPort, protocolType, meterAddr, meterPwd, priceCnt, decFormat, parentAddr, topCat,
            detailCat, configured);
    }

    /**
     * 远传表计装置序号：数值范围 1～2040。
     * 
     * @return the meterNo
     */
    public short getMeterNo() {
        return meterNo;
    }

    /**
     * 远传表计装置序号：数值范围 1～2040。
     * 
     * @param meterNo
     *            the meterNo to set
     */
    public void setMeterNo(short meterNo) {
        this.meterNo = meterNo;
    }

    /**
     * 所属测量点号：数值范围 0～2040，如为“0”，表示本序号的远传表计装置未指向任何测量点，
     * 即被“删除”。
     * 
     * @return the daNo
     */
    public short getDaNo() {
        return daNo;
    }

    /**
     * 所属测量点号：数值范围 0～2040，如为“0”，表示本序号的远传表计装置未指向任何测量点，
     * 即被“删除”。
     * 
     * @param daNo
     *            the daNo to set
     */
    public void setDaNo(short daNo) {
        this.daNo = daNo;
    }

    /**
     * @return the baudPort
     */
    public BaudPortField getBaudPort() {
        return baudPort;
    }

    /**
     * @param baudPort
     *            the baudPort to set
     */
    public void setBaudPort(BaudPortField baudPort) {
        if (baudPort == null) {
            throw new IllegalArgumentException("baud_port can't be null");
        }
        this.baudPort = baudPort;
    }

    /**
     * @return the protocolType
     */
    public ProtocolType getProtocolType() {
        return protocolType;
    }

    /**
     * @param protocolType
     *            the protocolType to set
     */
    public void setProtocolType(ProtocolType protocolType) {
        if (protocolType == null) {
            throw new IllegalArgumentException("protocol type can't be null");
        }
        this.protocolType = protocolType;
    }

    /**
     * 通信地址 见附录 C.33 7
     * 通信地址：数值范围 0～999999999999。
     * 
     * @return the meterAddr
     */
    public StdBcdNumber getMeterAddr() {
        return meterAddr;
    }

    /**
     * 通信地址 见附录 C.33 7
     * 通信地址：数值范围 0～999999999999。
     * 
     * @param meterAddr
     *            the meterAddr to set
     */
    public void setMeterAddr(StdBcdNumber meterAddr) {
        this.meterAddr = meterAddr;
    }

    /**
     * 通信密码 BIN 6
     * 通信密码：终端与远传表通信的密码。
     * 
     * @return the meterPwd
     */
    public HexString getMeterPwd() {
        return meterPwd;
    }

    /**
     * 通信密码 BIN 6
     * 通信密码：终端与远传表通信的密码。
     * 
     * @param meterPwd
     *            the meterPwd to set
     */
    public void setMeterPwd(HexString meterPwd) {
        this.meterPwd = meterPwd;
    }

    /**
     * @return the priceCnt
     */
    public byte getPriceCnt() {
        return priceCnt;
    }

    /**
     * @param priceCnt
     *            the priceCnt to set
     */
    public void setPriceCnt(byte priceCnt) {
        if (priceCnt < 1 || priceCnt > 48) {
            throw new IllegalArgumentException("price count should be [1, 48] but: " + priceCnt);
        }
        this.priceCnt = priceCnt;
    }

    /**
     * 计量示值的整数位及小数位个数：
     * ● D7～D4：备用
     * ● D3～D2：编码表示通信接入的远传表计示值的整数位个数，数值范围 0～3 依次表示 5～8 位整数。
     * ● D1～D0：编码表示通信接入的远传表计示值的小数位个数，数值范围 0～3 依次表示 1～4 位小数。
     * 
     * @return the decFormat
     */
    public DecFormat getDecFormat() {
        return decFormat;
    }

    /**
     * 计量示值的整数位及小数位个数：
     * ● D7～D4：备用
     * ● D3～D2：编码表示通信接入的远传表计示值的整数位个数，数值范围 0～3 依次表示 5～8 位整数。
     * ● D1～D0：编码表示通信接入的远传表计示值的小数位个数，数值范围 0～3 依次表示 1～4 位小数。
     * 
     * @param decFormat
     *            the decFormat to set
     */
    public void setDecFormat(DecFormat decFormat) {
        if (decFormat == null) {
            throw new IllegalArgumentException("decFormat can't be null");
        }
        this.decFormat = decFormat;
    }

    /**
     * 所属采集器通信地址 见附录 C.12 6
     * 所属采集器通信地址：数值范围 0～999999999999，如为“0”，表示本远传表未通过采集器连
     * 接到终端（或称“台区集中器”），而是直接连接到了终端（或称“台区集中器”）。
     * 
     * @return the parentAddr
     */
    public StdBcdNumber getParentAddr() {
        return parentAddr;
    }

    /**
     * 所属采集器通信地址 见附录 C.12 6
     * 所属采集器通信地址：数值范围 0～999999999999，如为“0”，表示本远传表未通过采集器连
     * 接到终端（或称“台区集中器”），而是直接连接到了终端（或称“台区集中器”）。
     * 
     * @param parentAddr
     *            the parentAddr to set
     */
    public void setParentAddr(StdBcdNumber parentAddr) {
        this.parentAddr = parentAddr;
    }

    /**
     * @return the topCat
     */
    public UserTopCategory getTopCat() {
        return topCat;
    }

    /**
     * @param topCat
     *            the topCat to set
     */
    public void setTopCat(UserTopCategory topCat) {
        if (topCat == null) {
            throw new IllegalArgumentException("top category can't be null");
        }
        this.topCat = topCat;
    }

    /**
     * @return the detailCat
     */
    public UserDetailCategory getDetailCat() {
        return detailCat;
    }

    /**
     * @param detailCat
     *            the detailCat to set
     */
    public void setDetailCat(UserDetailCategory detailCat) {
        if (detailCat == null) {
            throw new IllegalArgumentException("detail category can't be null");
        }
        this.detailCat = detailCat;
    }

    /**
     * @return the configured
     */
    public boolean isConfigured() {
        return configured;
    }

    /**
     * @param configured
     *            the configured to set
     */
    public void setConfigured(boolean configured) {
        this.configured = configured;
    }
}
